package ru.lukyanets.transmission.client.ui.controller;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.util.StringConverter;
import ru.lukyanets.transmission.client.connection.ConnectionInfo;
import ru.lukyanets.transmission.protocol.client.builder.HostInfo;

import java.net.URL;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;

/**
 * @author slukyanets
 */
public class ConnectionsDialogController implements Initializable {

    private final ObservableList<ConnectionInfo> connections = FXCollections.observableArrayList();

    @FXML
    private ChoiceBox<ConnectionInfo> connectionSelector;
    @FXML
    private TextField host;
    @FXML
    private Spinner<Integer> port;
    @FXML
    private CheckBox useSSL;
    @FXML
    private TextField username;
    @FXML
    private PasswordField password;
    @FXML
    private CheckBox useProxy;
    @FXML
    private TextField proxyHost;
    @FXML
    private Spinner<Integer> proxyPort;
    @FXML
    private TextField proxyUsername;
    @FXML
    private PasswordField proxyPassword;
    @FXML
    private Button buttonCreate;
    /** @noinspection unused*/
    @FXML
    private Button buttonRename;
    @FXML
    private Button buttonDelete;

    @FXML
    private HBox selectorButtons;
    @FXML
    private HBox editButtons;

    @FXML
    private TextField editName;
    @FXML
    private Button editOkButton;
    @FXML
    private Button editCancelButton;

    private ConnectionInfo selectedValue;


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        connectionSelector.setItems(connections);
        connectionSelector.getSelectionModel().selectedItemProperty().addListener((ob, oldVal, newVal) -> onChange());
        connectionSelector.setConverter(new ConnectionInfoConverter());

        proxyHost.disableProperty().bind(useProxy.selectedProperty().not());
        proxyPort.disableProperty().bind(useProxy.selectedProperty().not());
        proxyUsername.disableProperty().bind(useProxy.selectedProperty().not());
        proxyPassword.disableProperty().bind(useProxy.selectedProperty().not());

        buttonCreate.setOnAction(this::create);
        buttonDelete.setOnAction(this::delete);
        editCancelButton.setOnAction((e) -> cancelAdd());

        host.textProperty().addListener((x, o, n) -> extractData(selectedValue));
        port.valueProperty().addListener((x, o, n) -> extractData(selectedValue));
        useSSL.selectedProperty().addListener((x, o, n) -> extractData(selectedValue));
        username.textProperty().addListener((x, o, n) -> extractData(selectedValue));
        password.textProperty().addListener((x, o, n) -> extractData(selectedValue));
        useProxy.selectedProperty().addListener((x, o, n) -> extractData(selectedValue));
        proxyHost.textProperty().addListener((x, o, n) -> extractData(selectedValue));
        proxyPort.valueProperty().addListener((x, o, n) -> extractData(selectedValue));
        proxyUsername.textProperty().addListener((x, o, n) -> extractData(selectedValue));
        proxyPassword.textProperty().addListener((x, o, n) -> extractData(selectedValue));
    }

    public List<ConnectionInfo> getConnections() {
        return connections;
    }

    public void setConnections(List<ConnectionInfo> connections) {
        switchToEdit(false);
        this.connections.clear();
        this.connections.addAll(connections);
        if (connections.size() > 0) {
            connectionSelector.setValue(connections.get(0));
            selectedValue = connectionSelector.getValue();
        }
    }

    private void onChange() {
        selectedValue = null;
        populateData(connectionSelector.getValue());
        selectedValue = connectionSelector.getValue();
    }

    private void create(ActionEvent event) {
        editName.setText("");
        ConnectionInfo tmp = new ConnectionInfo(editName.getText());
        connections.add(tmp);
        connectionSelector.setValue(tmp);
        switchToEdit(true);
        editOkButton.setOnAction(e -> {
            connections.remove(tmp);
            ConnectionInfo connection = new ConnectionInfo(editName.getText());
            connections.add(connection);
            connectionSelector.setValue(connection);
            switchToEdit(false);
        });
    }

    private void cancelAdd() {
        delete(null);
        switchToEdit(false);
    }

    private void delete (ActionEvent event) {
        ConnectionInfo connectionInfo = connectionSelector.getValue();
        connections.remove(connectionInfo);
        connectionSelector.setValue(connections.stream().findFirst().orElse(null));
        selectedValue = connectionSelector.getValue();
    }

    private void extractData(ConnectionInfo connection) {
        if (connection == null) return;
        if (connection.getHost() == null) {
            connection.setHost(new HostInfo(host.getText(), port.getValue()));
        } else {
            connection.getHost().setHost(host.getText());
            connection.getHost().setPort(port.getValue());
        }
        connection.getHost().setUsername(username.getText());
        connection.getHost().setPassword(password.getText());
        connection.setUseSsl(useSSL.isSelected());
        if (useProxy.isSelected()) {
            if (connection.getProxyHost() == null) {
                connection.setProxyHost(new HostInfo(proxyHost.getText(), proxyPort.getValue()));
            } else {
                connection.getProxyHost().setHost(proxyHost.getText());
                connection.getProxyHost().setPort(proxyPort.getValue());
            }
            connection.getProxyHost().setUsername(proxyUsername.getText());
            connection.getProxyHost().setPassword(proxyPassword.getText());
        }
    }

    private void populateData(ConnectionInfo connection) {
        if (connection == null) return;
        host.setText(Optional.ofNullable(connection.getHost()).map(HostInfo::getHost).orElse(null));
        port.getValueFactory().setValue(Optional.ofNullable(connection.getHost()).map(HostInfo::getPort).orElse(9091));
        useSSL.setSelected(connection.isUseSsl());
        username.setText(Optional.ofNullable(connection.getHost()).map(HostInfo::getUsername).orElse(null));
        password.setText(Optional.ofNullable(connection.getHost()).map(HostInfo::getPassword).orElse(null));
        if (connection.getProxyHost() != null) {
            useProxy.setSelected(true);
            proxyHost.setText(connection.getProxyHost().getHost());
            proxyPort.getValueFactory().setValue(connection.getProxyHost().getPort());
            proxyUsername.setText(connection.getProxyHost().getUsername());
            proxyPassword.setText(connection.getProxyHost().getPassword());
        } else {
            useProxy.setSelected(false);
        }
    }

    private void switchToEdit(boolean show) {
        selectorButtons.setVisible(!show);
        connectionSelector.setVisible(!show);

        editName.setVisible(show);
        editButtons.setVisible(show);
    }

    private static class ConnectionInfoConverter extends StringConverter<ConnectionInfo> {

        @Override
        public String toString(ConnectionInfo object) {
            return object.getName();
        }

        @Override
        public ConnectionInfo fromString(String string) {
            throw new RuntimeException("Unsupported operation!");
        }
    }
}
